/**
 * 
 */
package eu.jakubiak.dto.manager;

import java.util.Collection;

import eu.jakubiak.dto.annotations.DataTransferObject;
import eu.jakubiak.dto.annotations.DtoAllow;
import eu.jakubiak.dto.annotations.DtoAllows;
import eu.jakubiak.dto.annotations.DtoDenies;
import eu.jakubiak.dto.annotations.DtoDeny;
import eu.jakubiak.dto.copy.DtoCopyHelper;
import eu.jakubiak.dto.enums.DtoPolicyType;
import eu.jakubiak.javabean.BeanProperty;
import eu.jakubiak.javabean.BeanReflactorCachedFactory;
import eu.jakubiak.javabean.BeanReflector;
import eu.jakubiak.javabean.IBeanReflectorFactory;

/**
 * Domyślna implementacja mamagera DTO
 * 
 * @author Antek
 *
 */
public class DtoManager implements IDtoManager {

	/**
	 * Cached Java Bean Reflector factory
	 */
	private static IBeanReflectorFactory beanReflectorFactory = BeanReflactorCachedFactory.instance();
	
	/**
	 * Context 
	 */
	private String context;
	/**
	 * Get current context
	 * @return
	 */
	public String getContext() {
		return context;
	}

	/**
	 * Create DTO managere in global context
	 */
	public DtoManager() {
		this("");
	}

	/**
	 * Create DTO Manager in specified context
	 * @param context
	 */
	public DtoManager(String context) {
		super();
		this.context = context;
	}
	
	/**
	 * Create DTO Manager in spcecifiec class and operation
	 * @param contextClass
	 * @param contextOperationName
	 */
	@SuppressWarnings("unchecked")
	public DtoManager(Class contextClass, String contextOperationName) {
		this(contextClass.getName()+"."+contextOperationName);
	}





	/* (non-Javadoc)
	 * @see eu.jakubiak.dto.IDtoManager#create(java.lang.Class, java.lang.Object)
	 */
	public <T> T copy(Class<T> clazz, T src, T dest) {
		// jeżeli nie ma użytej adnotacji do Dto to zwracamy obiekt źródłowy
		if(!clazz.isAnnotationPresent(DataTransferObject.class)) {
			return src;
		}
	
		DataTransferObject dto = clazz.getAnnotation(DataTransferObject.class);
		
		// get all java bean properties
		BeanReflector br = beanReflectorFactory.create(clazz);
		Collection<BeanProperty> bps = br.getProperties();
		if(DtoPolicyType.IMPLICIT.equals(dto.value())) {
			// kopiowanie wszystkie, co nie jest zabronione
			for(BeanProperty bp : bps) {
				if(!isDeny(bp)) {
					proportyCopy(bp, src, dest);
				}
			}
		} else {
			// kopiowanie tylko tego, co zostalo wskazane do kopiowanie 
			for(BeanProperty bp : bps) {
				if(isAllow(bp)) {
					proportyCopy(bp, src, dest);	
				}
			}
				
		}
		return dest;
	}
	
	/**
	 * Sprawdzamy, czy nie jest zabronione
	 * 
	 * @param property
	 * @return
	 */
	private boolean isDeny(BeanProperty property) {
		if(property.isAnnotationPresent(DtoDeny.class)) {
			if(testContext(property.getAnnotation(DtoDeny.class).value())) {
				return true;
			}
		}
		if(property.isAnnotationPresent(DtoDenies.class)) {
			for(DtoDeny deny : property.getAnnotation(DtoDenies.class).value()) {
				if(testContext(deny.value())) {
					return true;
				}
			}
		}
		return false;
	}
	
	/**
	 * Sprawdzamy, czy jest dozwolone
	 * 
	 * @param property
	 * @return
	 */
	private boolean isAllow(BeanProperty property) {
		if(property.isAnnotationPresent(DtoAllow.class)) {
			if(testContext(property.getAnnotation(DtoAllow.class).value())) {
				return true;
			}
		}
		if(property.isAnnotationPresent(DtoAllows.class)) {
			for(DtoAllow allow : property.getAnnotation(DtoAllows.class).value()) {
				if(testContext(allow.value())) {
					return true;
				}
			}
		}
		return false;
	}

	
	/**
	 * Sprawdzamy czy context manegara pasuje do contextu testowanego
	 * @param against
	 * @return
	 */
	private boolean testContext(String against) {
		return getContext().startsWith(against);
	}
	

	/**
	 * recurion property copying 
	 * 
	 * @param property
	 * @param from
	 * @param into
	 * @return 
	 * @return 
	 */
	private <T> void proportyCopy(BeanProperty property, T from, T into) {
		new DtoCopyHelper().copy(this, property, from, into);
	}
	

}
